package com.gsoc.spreadsheet.client;

/**
 * A cluster of cells (3x3) that will be loaded and unloaded as necessary,
 * depending on the scrolling of the user.
 */
import com.google.gwt.core.client.GWT;
import com.google.gwt.user.client.rpc.AsyncCallback;
import com.google.gwt.user.client.rpc.ServiceDefTarget;
import com.google.gwt.user.client.rpc.IsSerializable;
import com.google.gwt.user.client.ui.Button;
import com.google.gwt.user.client.ui.Composite;
import com.google.gwt.user.client.ui.Grid;
import com.google.gwt.user.client.ui.TextBox;

public class CellCluster extends Composite implements ClusterRPCAsync {

	/**
	 * The square root of the number of cells that will be contained
	 * in each cluster. This number is used to calculate absolute
	 * cell position so it must be kept final.
	 */
	public static final int size = 3;
	
	/**
	 * This cluster's X coordinate relative to other clusters.
	 */
	private int clusX;
	
	/**
	 * This cluster's Y coordinate relative to other clusters.
	 */
	private int clusY;
	
	/**
	 * The data cells associated with this cluster
	 */
	private DataCell[][] grid;
	
	/**
	 * The Grid object that will ultimately be displayed in the UI
	 */
	private Grid display = new Grid(size, size);
	
	/**
	 * The type of service this will be looking for server-side
	 */
	private ClusterRPCAsync service = (ClusterRPCAsync) GWT.create(ClusterRPC.class);
	
	/**
	 * The target location of where to begin looking for the server-side service.
	 * This is how to implement RPCs.
	 */
	private ServiceDefTarget endpoint = (ServiceDefTarget) service;
	
//	/**
//	 * Class constructor
//	 */
//	public CellCluster() {
//		grid = new DataCell[size][size];
//		for(int r = 0; r < size; ++r) {
//			for(int c = 0; c < size; ++c) {
//				setCell(r, c, new DataCell(r, c));
//			}
//		}
//		
//		initDisplay();
//	}
	
	/**
	 * Class constructor specifying where this cluster is relative to other clusters.
	 * 
	 * @param thisX 	this cluster's X coordinate
	 * @param thisY 	this cluster's Y coordinate
	 */
	public CellCluster(int thisX, int thisY) {
		
		clusX = thisX;
		clusY = thisY;
		
		endpoint.setServiceEntryPoint(GWT.getModuleBaseURL() + "rpc");
		
		loadCluster(thisX, thisY, size, new AsyncCallback<Double[][]>() {
			
			// Be sure to check array length, and compare to cluster size.
			public void onSuccess(Double[][] result) {
				grid = new DataCell[result.length][result.length];
				for(int r = 0; r < result.length; ++r) {
					for(int c = 0; c < result.length; ++c) {
						grid[r][c] = new DataCell(r, c, result[r][c]);
						display.setWidget(r, c, grid[r][c].view());
					}
				}
				
				((TextBox)display.getWidget(0, 0)).setText("Success!");
			}
			
			public void onFailure(Throwable caught) {
				grid = new DataCell[size][size];
				for(int r = 0; r < size; ++r) {
					for(int c = 0; c < size; ++c) {
						grid[r][c] = new DataCell(r, c);
						display.setWidget(r, c, grid[r][c].view());
					}
				}
//				Window.alert(caught.getMessage());
//				((TextBox)display.getWidget(0, 0)).setText(caught.getMessage());
			}
		});
	}
	
//	Using serializable clusters:
//	public CellCluster(int thisX, int thisY) {
//		
//		clusX = thisX;
//		clusY = thisY;
//		
//		endpoint.setServiceEntryPoint(GWT.getModuleBaseURL() + "rpc");
//		loadCluster(thisX, thisY, new AsyncCallback() {
//			
//			public void onSuccess(Object result) {
//				grid = new DataCell[size][size];
//				for(int row = 0; row < size; ++row) {
//					for(int col = 0; col < size; ++col) {
//						grid[row][col] = ((CellCluster)result).getCell(row, col);
//						display.setWidget(row, col, grid[row][col].view());
//					}
//				}
//			}
//			
//			public void onFailure(Throwable caught) {
//				grid = new DataCell[size][size];
//				for(int r = 0; r < size; ++r) {
//					for(int c = 0; c < size; ++c) {
//						grid[r][c] = new DataCell(r, c);
//						display.setWidget(r, c, grid[r][c].view());
//					}
//				}
//			}
//		});
//	}
	
	/**
	 * Returns the DataCell at the specified location in the cluster.
	 * 
	 * @param row	the cell's row
	 * @param col	the cell's column
	 * @return		the data cell at the specified coordinates
	 */
	public DataCell getCell(int row, int col) {
		return grid[row][col];
	}
	
	private void initDisplay() {
		 
		int displaySize = size + 1;
		display = new Grid(displaySize, displaySize);
		display.setCellPadding(0);
		display.setCellSpacing(0);
		
		Button bu = new Button("$");
		bu.addStyleName("select-button");
		display.setWidget(0, 0, bu);
		
		// Set the top row to be column select buttons
		for(int c = 1; c <= size; ++c) {
			display.setWidget(0, c, new Button(c + ""));
		}
		
		for(int r = 1; r <= size; ++r) {
			display.setWidget(r, 0, new Button(r + ""));
		}
		
		// Set the rest of the grid
		for(int r = 1; r <= size; ++r) {
			for(int c = 1; c <= size; ++c) {
				display.setWidget(r, c, grid[r-1][c-1].view());
			}
		}
	}
	
	/**
	 * Returns this cluster's Grid for the UI
	 * 
	 * @return	Grid representing this cluster
	 */
	public Grid view() {
		return display;
	}
	
	/**
	 * Returns a two-dimensional array of the all the data within the DataCells
	 * in this cluster
	 * 
	 * @return	array of the doubles contained in DataCells
	 */
	public Double[][] toArray() {
		Double[][] array = new Double[size][size];
		for(int r = 0; r < size; ++r) {
			for(int c = 0; c < size; ++c) {
				array[r][c] = grid[r][c].getData();
			}
		}
		
		return array;
	}
	
//	public void selectCol(int col) {
//		int c = col - 1;
//		if(selCol != -1) {
//			for(int r = 1; r < size; ++r) {
//				grid[r-1][c].removeStyleName("selected");
//			}
//		}
//		
//		for(int r = 1; r < size; ++r) {
//			grid[r-1][c].addStyleName("selected");
//		}
//		selCol = col;
//	}
//	
//	public void selectRow(int row) {
//		int r = row - 1;
//		if(selRow != -1) {
//			for(int c = 1; c < size; ++c) {
//				grid[r][c-1].removeStyleName("selected");
//			}
//		}
//		
//		for(int c = 1; c < size; ++c) {
//			grid[r][c-1].addStyleName("selected");
//		}
//		selRow = row;
//	}
//	
	
	/**
	 * RPC call to save this cluster server-side
	 */
	public void saveCluster(int thisX, int thisY, Double[][] data, AsyncCallback callback) {
		service.saveCluster(thisX, thisY, data, callback);
	}
	
	/**
	 * RPC call to retrieve this cluster from the server
	 */
	public void loadCluster(int thisX, int thisY, int size, AsyncCallback callback ) {
		service.loadCluster(thisX, thisY, size, callback);
	}
	
//	public void saveCluster(CellCluster toSave, AsyncCallback callback) {
//		service.saveCluster(this, callback);
//	}
}
